/*
 * Copyright (c) 2011 Jean-Marc Perronne
 * Ensisa, 12 rue des Freres Lumieres, F-68093 Mulhouse cedex
 * All rights reserved.
 *
 * The copyright of this software is owned by Werner Randelshofer. 
 * You may not use, copy or modify this software, except in  
 * accordance with the license agreement you entered into with  
 * Jean-Marc Perronne. For details see accompanying license terms. 
 */

package fr.uha.ensisa.jmp.drone.handler;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.PrintStream;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import fr.uha.ensisa.jmp.drone.app.DroneConstants;
import fr.uha.ensisa.jmp.drone.at_commands.AtCommand;
import fr.uha.ensisa.jmp.drone.at_commands.AtCommands;
import fr.uha.ensisa.jmp.drone.boot.BootAction;
import fr.uha.ensisa.jmp.drone.boot.BootController;
import fr.uha.ensisa.jmp.drone.boot.BootException;
import fr.uha.ensisa.jmp.drone.com.BadInterfaceException;
import fr.uha.ensisa.jmp.drone.configuration.ConfigConsumer;
import fr.uha.ensisa.jmp.drone.configuration.Configurations;
import fr.uha.ensisa.jmp.drone.input.progressive.FlightInputs;
import fr.uha.ensisa.jmp.drone.navdata.Navdata;
import fr.uha.ensisa.jmp.drone.navdata.options.OptionsVistor;
import fr.uha.ensisa.jmp.drone.recorder.configuration.DroneConfigRecorder;
import fr.uha.ensisa.jmp.drone.video.ImageConsumer;


public class DroneHandler extends Handler
{
	private NavdataHandler navHandler;
	private AtCommandHandler atHandler;
	private VideoHandler videoHandler;
	private ConfigHandler configHandler;
	
	private BootController bootController;

	private Map<String, OptionsVistor> optionVisitors;
	private Map<String,ImageConsumer> imageConsumers;
	private Map<String,ConfigConsumer> configConsumers;

	private Configurations currentConfiguration;
	
	public DroneHandler(FlightInputs input) throws BadInterfaceException, SocketException, UnknownHostException
	{
		this.atHandler = new AtCommandHandler(this, input);
		this.bootController = new BootController(this);
		this.navHandler = new NavdataHandler(this);
		this.videoHandler = new VideoHandler(this);
		this.configHandler = null;
		
		this.optionVisitors = new HashMap<String, OptionsVistor>();
		this.imageConsumers = new HashMap<String,ImageConsumer>();
		this.configConsumers = new HashMap<String,ConfigConsumer>();

		this.registerConfigConsumer(DroneConstants.DRONE_CONFIG_RECORDER_KEY, new DroneConfigRecorder());
		this.currentConfiguration = new Configurations();
	}

	public synchronized void registerOptionVisitor(String key, OptionsVistor v)
	{
		this.optionVisitors.put(key, v);
	}
	
	public synchronized void unregisterOptionVisitor(String key)
	{
		this.optionVisitors.remove(key);
	}
	
	public synchronized OptionsVistor getOptionVisitor(String key)
	{
		return this.optionVisitors.get(key);
	}

	public synchronized void registerImageConsumer(String key, ImageConsumer c)
	{
		this.imageConsumers.put(key, c);	
	}
	
	public synchronized void unregisterImageConsumer(String key)
	{
		this.imageConsumers.remove(key);
	}

	public synchronized ImageConsumer getImageConsumer(String key)
	{
		return this.imageConsumers.get(key);
	}

	public synchronized void registerConfigConsumer(String key, ConfigConsumer c)
	{
		this.configConsumers.put(key, c);	
	}
	
	public synchronized void unregisterConfigConsumer(String key)
	{
		this.configConsumers.remove(key);
	}

	public synchronized ConfigConsumer getConfigConsumer(String key)
	{
		return this.configConsumers.get(key);
	}

	public synchronized void sendScheduled(AtCommand cde)
	{
		this.atHandler.sendScheduled(cde);
	}

	public void sendAndDumpMode(PrintStream stream)
	{
		this.atHandler.sendAndDumpMode(stream);
	}

	public synchronized void sendDumpOnlyMode(PrintStream stream)
	{
		this.atHandler.sendDumpOnlyMode(stream);
	}
	
	public synchronized void sendOnlyMode()
	{
		this.atHandler.sendOnlyMode();
	}

	public synchronized void sendImmediately(AtCommand cde) throws IOException
	{
		this.atHandler.sendImmediately(cde);
	}

	public synchronized void resetSequenceNumber()
	{
		this.atHandler.resetSequenceNumber();
		
	}
	public synchronized void addBootAction(BootAction a)
	{
		this.bootController.addBootAction(a);
	}
	
	public synchronized boolean booted()
	{
		return this.bootController.booted();
	}
	
	public synchronized String getConfiguration(String key)
	{
		return this.currentConfiguration.value(key);
	}
	
	private ConfigHandler getConfigHandler() throws SocketException, UnknownHostException, BadInterfaceException
	{
		ConfigHandler tmp = configHandler  = new  ConfigHandler(this);
		tmp.start();
		return tmp;
	}

	@Override
	public void run()
	{
		try
		{
			this.running();

			this.addBootAction(new BootAction()
			{
				public boolean doBootAction()
				{
					if (configHandler != null )
					{	
						configHandler.terminate();
						configHandler = null;
					}
					return true;
				}
			});
			
			this.atHandler.start();
			this.navHandler.start();
			this.videoHandler.start();

			while (this.navHandler.isActive() && this.atHandler.isActive() && this.isActive())
			{
				Navdata navdata = this.navHandler.data().poll();
				if (navdata != null)
				{
					this.bootController.control(navdata);

					for (OptionsVistor v : this.optionVisitors.values())
						navdata.accept(v);
				}

				if (this.bootController.booted())
				{
					BufferedImage image = this.videoHandler.data().poll();
					if (image != null)
					{
						for (ImageConsumer c : this.imageConsumers.values())
							c.consume(image);
					}
	
					if (this.configHandler == null) 
					{
							this.configHandler = this.getConfigHandler();
							this.sendScheduled(AtCommands.GET_CONFIGURATION);
					}
	
					String[] config = this.configHandler.data().poll();
					if (config != null)
					{
						for (ConfigConsumer c : this.configConsumers.values())
							c.consume(config);
						/*
						synchronized(this.currentConfiguration)
						{
							this.currentConfiguration.put(config[0], config[1]);
							System.out.println(config[0] + "=" + config[1]);
						}*/
					}
				}
					
				Thread.yield();
			}
		}
		catch (BootException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			this.notRrunning();
			this.close();
		}		
	}
	
	@Override
	public void close()
	{		
		this.videoHandler.terminate();
		this.configHandler.terminate();
		this.navHandler.terminate();
		this.atHandler.terminate();
		
		this.videoHandler.shutdown();
		this.configHandler.shutdown();
		this.navHandler.shutdown();
		this.atHandler.shutdown();
		
		System.out.println("by by...");
	}
}
